#ifndef __EPOLL_HPP__
#define __EPOLL_HPP__

#include <sys/epoll.h>
#include <unistd.h>
#include "log.hpp"

// 创建epoll模型，使用此epoll模型进行操作
class Epoll
{
    const static int gsize = 256;
    const static int gtimeout = 10000;  // 10s
private:
    int _epfd;
public:
    Epoll():_epfd(-1)
    {}

    // 创建epoll模型
    bool EpollCreate()
    {
        _epfd = epoll_create(gsize);  // gsize需要注意是一个废弃的参数，这里随便设置即可
        if (_epfd < 0) logMessage(FATAL, "epoll_create error %d:%s", errno, strerror(errno));
        return _epfd != -1;
    }

    // 增添监听对象到epoll模型中来，并且说明需要关心的事件 如果需要使用ET模式进行监听，需要在event选项添加EPOLLET,需要注意，默认就是LT模式
    bool AddSockToEpoll(int sock, int event)
    {
        struct epoll_event epev;
        epev.events = event;
        epev.data.fd = sock;

        int flag = epoll_ctl(_epfd, EPOLL_CTL_ADD, sock, &epev);
        if (flag < 0) logMessage(FATAL, "epoll_ctl error %d:%s", errno, strerror(errno));
        return flag != -1;
    }

    // 修改sock在epoll所关心的状态
    bool CtlSockToEpoll(int sock, int event)
    {
        // 进来先添加ET模式
        event |= EPOLLET;
        struct epoll_event epev;
        epev.events = event;
        epev.data.fd = sock;

        int flag = epoll_ctl(_epfd, EPOLL_CTL_MOD, sock, &epev);
        if (flag < 0) logMessage(FATAL, "epoll_ctl error %d:%s", errno, strerror(errno));
        return flag != -1;
    }

    // 删除sock在epoll对应的监听
    bool DelFromEpoll(int sock)
    {
        int flag = epoll_ctl(_epfd, EPOLL_CTL_DEL, sock, nullptr);
        if (flag < 0) logMessage(FATAL, "epoll_ctl error %d:%s", errno, strerror(errno));
        return flag != -1;
    }

    // 从epoll模型的就绪队列取出num个(num是期望个数)，并且以timeout毫秒的时候进行一次阻塞。-1为阻塞，0非阻塞，默认5s一次阻塞
    // 需要注意的是，如果是ET模型，只有等待对象的缓存区出现变化后才会通知，并且只通知一次，如果通知后没有取走或者取完，此就绪队列就不会存储其相关的就绪事件了
    int WaitEpoll(epoll_event events[], int num, int timeout = gtimeout)  // 默认5s进行进行一次阻塞
    {
        return epoll_wait(_epfd, events, num, timeout);
    }

    ~Epoll()
    {
        if (_epfd >= 0) close(_epfd);
    }
};

#endif